package com.example.whereismystuff.classes;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Calendar;
import java.util.Hashtable;

public class UserDataManager implements Manager<User> 
{
	private static UserDataManager m_UserDataManager = new UserDataManager();
	
	private int m_nUserIDNext = 0;
	
	private File m_file;
	
	private Hashtable<String, Integer> m_htUserNames;
	private Hashtable<Integer, Integer> m_htUserIDs;
	private ArrayList<User> m_alUsers;
	
	protected UserDataManager()
	{
		m_htUserNames = new Hashtable<String, Integer>();
		m_htUserIDs = new Hashtable<Integer, Integer>();
		m_alUsers = new ArrayList<User>();
		
	}
	
	private void AssociateItemsWithUsers()
	{
		ItemDataManager idmItemManager = ItemDataManager.getItemDataManager();
		
		for (int n = 0; n < idmItemManager.getDatabaseSize(); ++n)
		{
			Item loopItem = idmItemManager.getItem(n);
			//int nPosterID = loopItem.getPosterID();
			//loopItem.setPoster(m_alUsers.get(nPosterID));
		}
	}
	
	public static UserDataManager getUserDataManager()
	{
		return m_UserDataManager;
	}
	
	/**
	 * Add the user to the database
	 * @param User
	 */
	@Override
	public void addToDatabase(User user)
	{
		if (getUser(user.getUserName()) != null)
			return;
		
		user.setUniqueID(m_nUserIDNext);
		++m_nUserIDNext;
		
		m_htUserNames.put(user.getUserName(), m_alUsers.size());
		m_htUserIDs.put(user.getUniqueID(), m_alUsers.size());
		
		m_alUsers.add(user);
		
		WriteDataToDisk();
	}
	/**
	 * Removes an user from the database.
	 * @param User
	 * @return User that was removed
	 */
	@Override
	public User removeFromDatabase(User user)
	{
		int nIndex = m_alUsers.indexOf(user);
		if (nIndex == -1)
			return null;
		
		for (int n = nIndex+1; n < m_alUsers.size(); ++n)
		{
			User loopUser = m_alUsers.get(n);
			Integer nPreviousSpot = m_htUserNames.get(loopUser.getUserName());
			m_htUserNames.put(loopUser.getUserName(), nPreviousSpot-1);
			m_htUserIDs.put(loopUser.getUniqueID(), nPreviousSpot-1);
		}
		
		User removedUser = m_alUsers.get(nIndex);
		List<Item> items = removedUser.getItems();
		ItemDataManager itemManager = ItemDataManager.getItemDataManager();
		for (int n = 0; n < items.size(); ++n)
			itemManager.removeFromDatabase(items.get(n));
		
		m_alUsers.remove(nIndex);
		
		WriteDataToDisk();
		
		return removedUser;
	}
	/**
	 * Gets the user that is being searched for.
	 * Returns null if doesn't exist.
	 * @param username
	 * @return User
	 */
	public User getUser(String strUsername)
	{
		Integer nTemp = m_htUserNames.get(strUsername);
		//System.out.println(m_htUsers.get(strUsername));
		
		if (nTemp == null)
			return null;
		else
			return m_alUsers.get(nTemp);
	}
	
	public User getUser(int nID)
	{
		Integer nTemp = m_htUserIDs.get(nID);
		
		if (nTemp == null)
			return null;
		else
			return m_alUsers.get(nTemp);
	}
	
	public int getUserID(String strUsername)
	{
		User tempUser = getUser(strUsername);
		if (tempUser == null)
			return -1;
		else
			return tempUser.getUniqueID();
	}
	
	/**
	 * Checks database to see if user with the given username and password
	 * exist. If so return true.
	 * @param user
	 * @return if user is true.
	 */
	public boolean verifyUser(User user)
	{
		User tempUser = getUser(user.getUserName());
		if (tempUser == null)
			return false;
		else if (tempUser.getPassword().equals(user.getPassword()))
			return true;
		else
			return false;
	}
	
	/**
	 * Will sort the database.
	 * Method might not be implemented.
	 */
	public void sort()
	{
	}
	
	private void PopulateUserDatabase() throws IOException
	{
		FileReader fileReader = new FileReader(m_file.getAbsoluteFile());
		BufferedReader outputReader = new BufferedReader(fileReader);
		
		String strLine;
		String strCharSequence;
		User currentReadUser;
		ArrayList<Integer> items = new ArrayList<Integer>();
		String strTempUsername = "", strTempPassword = "", strTempEmail = "";
		int nTempUserID = 0;
		boolean bReadingItems = false;
		
		for (strLine = outputReader.readLine(); strLine != null; strLine = outputReader.readLine())
		{
			Scanner scanner = new Scanner(strLine);
			try
			{
				strCharSequence = scanner.next();
				//System.out.println(strCharSequence);
			}
			catch (NoSuchElementException endOfLine)
			{
				continue;
			}
			while (strCharSequence != null)
			{
				if (bReadingItems)
				{
					try
					{
						if (strCharSequence.equals("}"))
						{
							bReadingItems = false;
						}
						else if (strCharSequence.equals("{"))
						{
							// Do Nothing
						}
						else
						{
							Integer nItemID = Integer.parseInt(strCharSequence.trim());
							items.add(nItemID);
						}
						
						strCharSequence = scanner.next();
					}
					catch (NoSuchElementException endOfLine)
					{
						break;
					}
				}
				else
				{
					scanner.useDelimiter("line.seperator");
					try
					{
						if (strCharSequence.equals("$IDNext"))
						{
							strCharSequence = scanner.next().trim();
							m_nUserIDNext = Integer.parseInt(strCharSequence);
						}
						else if (strCharSequence.equals("$Date"))
						{
							strCharSequence = scanner.next().trim();
						}
						else if (strCharSequence.equals("$Username"))
						{
							strCharSequence = strTempUsername = scanner.next().trim();
						}
						else if (strCharSequence.equals("$UserID"))
						{
							String tempstring = scanner.next().trim();
							nTempUserID = Integer.parseInt(tempstring);
							strCharSequence = tempstring;
						}
						else if (strCharSequence.equals("$Password"))
						{
							strCharSequence = strTempPassword = scanner.next().trim();
						}
						else if (strCharSequence.equals("$Email"))
						{
							strCharSequence = strTempEmail = scanner.next().trim();
						}
						else if (strCharSequence.equals("$Items"))
						{
							bReadingItems = true;
							scanner.useDelimiter("\\p{javaWhitespace}+");
						}
						else if (strCharSequence.equals("}"))
						{
							currentReadUser = new User(strTempUsername, strTempPassword, strTempEmail);
							currentReadUser.setUniqueID(nTempUserID);
							ItemDataManager idmItemManager = ItemDataManager.getItemDataManager();
							for (int n = 0; n < items.size(); ++n)
							{
								Item loopItem = idmItemManager.getItem(items.get(n));
								currentReadUser.addItem(loopItem);
							}
							items.clear();
							m_alUsers.add(currentReadUser);
							m_htUserNames.put(currentReadUser.getUserName(), m_alUsers.size()-1);
							m_htUserIDs.put(currentReadUser.getUniqueID(), m_alUsers.size()-1);
						}
						
						strCharSequence = scanner.next();
					}
					catch (NoSuchElementException endOfLine)
					{
						break;
					}
				}
			}
		}
		
		outputReader.close();
		
		AlphabetiseArrayList();
	}
	
	public void WriteDataToDisk()
	{
		try
		{
			String strIndentString = "";
			
			FileWriter m_fileStream = new FileWriter(m_file);
			BufferedWriter m_bufferedWriter = new BufferedWriter(m_fileStream);
			PrintWriter m_outputWriter = new PrintWriter(m_bufferedWriter);
			
			m_outputWriter.println("$Date " + Calendar.getInstance().getTime());
			m_outputWriter.println("$IDNext " + m_nUserIDNext);
			m_outputWriter.println();
			
			for (int n = 0; n < m_alUsers.size(); ++n)
			{
				User loopUser = m_alUsers.get(n);
				m_outputWriter.println(strIndentString + "$Username " + loopUser.getUserName());
				m_outputWriter.println(strIndentString + "{");
				strIndentString = "\t";
				m_outputWriter.println(strIndentString + "$Password " + loopUser.getPassword());
				m_outputWriter.println(strIndentString + "$Email " + loopUser.getEmail());
				List<Item> loopItems = loopUser.getItems();
				
				m_outputWriter.print(strIndentString + "$Items { ");
				for (int m = 0; m < loopItems.size(); ++m)
					m_outputWriter.print(loopItems.get(m).getUniqueID() + " ");
				m_outputWriter.println("}");
				m_outputWriter.println(strIndentString + "$UserID " + n);
				strIndentString = "";
				m_outputWriter.println(strIndentString + "}");
				m_outputWriter.println();
			}
			
			m_outputWriter.close();
		}
		catch (IOException error)
		{
			System.out.println("Error: " + error.getMessage());
			error.printStackTrace();
		}
	}
	
	private void AlphabetiseArrayList()
	{
		// Will implement in a little bit
	}

	@Override
	public void updateDatabase(int nUniqueID, User user)
	{
		User tempUser = m_alUsers.get(m_htUserIDs.get(nUniqueID));
		if (tempUser == null)
			return;
		
		tempUser.setUserName(user.getUserName());
		tempUser.setEmail(user.getEmail());
		tempUser.setPassword(user.getPassword());
		
		WriteDataToDisk();
	}
	
	public String toString()
	{
		return "Users: " + m_alUsers.toString() + "\nUsernames: " + m_htUserNames.toString() + "\nUserIDs: " + m_htUserIDs.toString();
	}
	
	public void Initialize(File file)
	{
		m_file = file;
		
		try
		{
			PopulateUserDatabase();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
		AssociateItemsWithUsers();
	}
}
